# ThreadLocals.java

    package org.codefilarete.tool;

    import java.util.function.Consumer;
    import java.util.function.Function;
    import java.util.function.Supplier;

    import org.codefilarete.tool.function.Hanger;
    import org.codefilarete.tool.function.ThrowingConsumer;
    import org.codefilarete.tool.function.ThrowingFunction;
    import org.codefilarete.tool.function.ThrowingRunnable;
    import org.codefilarete.tool.function.ThrowingSupplier;

    /**
     * @author Guillaume Mary
     */
    public class ThreadLocals {

    	/**
    	 * Runs some code with a {@link ThreadLocal} and cleans it afterwards by removing the instance of the {@link ThreadLocal}
    	 * through {@link ThreadLocal#remove()}.
    	 *
    	 * @param threadLocal the {@link ThreadLocal} to be used
    	 * @param factory the supplier of the instance assigned to {@link ThreadLocal}
    	 * @param runnable some code to be run
    	 * @param <T> type of bean assigned to the ThreadLocal
    	 * @see AutoRemoveThreadLocal
    	 */
    	public static <T> void doWithThreadLocal(ThreadLocal<T> threadLocal, Supplier<T> factory, Runnable runnable) {
    		doWithThreadLocal(threadLocal, factory, (ThrowingRunnable<RuntimeException>) runnable::run);
    	}

    	/**
    	 * Runs some code with a {@link ThreadLocal} and cleans it afterwards by removing the instance of the {@link ThreadLocal}
    	 * through {@link ThreadLocal#remove()}.
    	 *
    	 * @param threadLocal the {@link ThreadLocal} to be used
    	 * @param factory the supplier of the instance assigned to {@link ThreadLocal}
    	 * @param runnable some code to be run
    	 * @param <T> type of bean assigned to the ThreadLocal
    	 * @see AutoRemoveThreadLocal
    	 */
    	public static <T, E extends Throwable> void doWithThreadLocal(ThreadLocal<T> threadLocal, Supplier<T> factory, ThrowingRunnable<E> runnable)
    			throws E {
    		doWithThreadLocal(threadLocal, factory, (ThrowingFunction<T, Void, E>) t -> { runnable.run(); return null; });
    	}

    	/**
    	 * Runs some code with a {@link ThreadLocal} and cleans it afterwards by removing the instance of the {@link ThreadLocal}
    	 * through {@link ThreadLocal#remove()}.
    	 *
    	 * @param threadLocal the {@link ThreadLocal} to be used
    	 * @param factory the supplier of the instance assigned to {@link ThreadLocal}
    	 * @param runnable some code to be run
    	 * @param <T> type of bean assigned to the ThreadLocal
    	 * @see AutoRemoveThreadLocal
    	 */
    	public static <T> void doWithThreadLocal(ThreadLocal<T> threadLocal, Supplier<T> factory, Consumer<T> runnable) {
    		doWithThreadLocal(threadLocal, factory, (ThrowingConsumer<T, RuntimeException>) runnable::accept);
    	}

    	/**
    	 * Runs some code with a {@link ThreadLocal} and cleans it afterwards by removing the instance of the {@link ThreadLocal}
    	 * through {@link ThreadLocal#remove()}.
    	 *
    	 * @param threadLocal the {@link ThreadLocal} to be used
    	 * @param factory the supplier of the instance assigned to {@link ThreadLocal}
    	 * @param runnable some code to be run
    	 * @param <T> type of bean assigned to the ThreadLocal
    	 * @see AutoRemoveThreadLocal
    	 */
    	public static <T, E extends Throwable> void doWithThreadLocal(ThreadLocal<T> threadLocal, Supplier<T> factory, ThrowingConsumer<T, E> runnable)
    			throws E {
    		doWithThreadLocal(threadLocal, factory, (ThrowingFunction<T, Void, E>) t -> { runnable.accept(t); return null; });
    	}

    	/**
    	 * Runs some code with a {@link ThreadLocal} and cleans it afterwards by removing the instance of the {@link ThreadLocal}
    	 * through {@link ThreadLocal#remove()}.
    	 *
    	 * @param threadLocal the {@link ThreadLocal} to be used
    	 * @param factory the supplier of the instance assigned to {@link ThreadLocal}
    	 * @param runnable some code to be run
    	 * @param <T> type of bean assigned to the ThreadLocal
    	 * @see AutoRemoveThreadLocal
    	 */
    	public static <T, O> O doWithThreadLocal(ThreadLocal<T> threadLocal, Supplier<T> factory, Supplier<O> runnable) {
    		return doWithThreadLocal(threadLocal, factory, (ThrowingSupplier<O, RuntimeException>) runnable::get);
    	}

    	/**
    	 * Runs some code with a {@link ThreadLocal} and cleans it afterwards by removing the instance of the {@link ThreadLocal}
    	 * through {@link ThreadLocal#remove()}.
    	 *
    	 * @param threadLocal the {@link ThreadLocal} to be used
    	 * @param factory the supplier of the instance assigned to {@link ThreadLocal}
    	 * @param runnable some code to be run
    	 * @param <T> type of bean assigned to the ThreadLocal
    	 * @see AutoRemoveThreadLocal
    	 */
    	public static <T, O, E extends Throwable> O doWithThreadLocal(ThreadLocal<T> threadLocal, Supplier<T> factory, ThrowingSupplier<O, E> runnable)
    			throws E {
    		return doWithThreadLocal(threadLocal, factory, (ThrowingFunction<T, O, E>) t -> runnable.get());
    	}

    	/**
    	 * Runs some code with a {@link ThreadLocal} and cleans it afterwards by removing the instance of the {@link ThreadLocal}
    	 * through {@link ThreadLocal#remove()}.
    	 *
    	 * @param threadLocal the {@link ThreadLocal} to be used
    	 * @param factory the supplier of the instance assigned to {@link ThreadLocal}
    	 * @param runnable some code to be run
    	 * @param <T> type of bean assigned to the ThreadLocal
    	 * @see AutoRemoveThreadLocal
    	 */
    	public static <T, O> O doWithThreadLocal(ThreadLocal<T> threadLocal, Supplier<T> factory, Function<T, O> runnable) {
    		return doWithThreadLocal(threadLocal, factory, (ThrowingFunction<T, O, RuntimeException>) runnable::apply);
    	}

    	/**
    	 * Runs some code with a {@link ThreadLocal} and cleans it afterwards by removing the instance of the {@link ThreadLocal}
    	 * through {@link ThreadLocal#remove()}.
    	 *
    	 * @param threadLocal the {@link ThreadLocal} to be used
    	 * @param factory the supplier of the instance assigned to {@link ThreadLocal}
    	 * @param runnable some code to be run
    	 * @param <T> type of bean assigned to the ThreadLocal
    	 * @see AutoRemoveThreadLocal
    	 */
    	public static <T, O, E extends Throwable> O doWithThreadLocal(ThreadLocal<T> threadLocal, Supplier<T> factory, ThrowingFunction<T, O, E> runnable)
    			throws E {
    		T value = threadLocal.get();
    		if (value == null) {
    			value = factory.get();
    			threadLocal.set(value);
    		}

    		try (AutoRemoveThreadLocal<T> ignored = new AutoRemoveThreadLocal<>(threadLocal)) {
    			return runnable.apply(value);
    		}
    	}

    	/**
    	 * A {@link ThreadLocal} that will be cleared ({@link ThreadLocal#remove()} method called) after usage as a try-with-resource.
    	 */
    	public static class AutoRemoveThreadLocal<T> implements AutoCloseable, Supplier<T>, Hanger<T> {

    		private final ThreadLocal<T> delegate;

    		public AutoRemoveThreadLocal(ThreadLocal<T> delegate) {
    			this.delegate = delegate;
    		}

    		@Override
    		public T get() {
    			return delegate.get();
    		}

    		@Override
    		public void set(T value) {
    			delegate.set(value);
    		}

    		@Override
    		public void close() {
    			delegate.remove();
    		}
    	}
    }